﻿module core.dll;

private import std.typetuple;
private import std.c.windows.windows;
private import std.traits;
private import std.string;
private import std.utf;

extern(Windows) {
    HMODULE LoadLibraryW(LPCWSTR libPath);
}

private static class ModuleManager {
    private static HMODULE  [char[]]    m_modules;

    private this() {}

    static public ~this() {
        foreach(h; m_modules) {
            FreeLibrary(h);
        }
    }

    private static HMODULE registerModule(char[] name) {
        char[] lname = tolower(name);
        HMODULE h = LoadLibraryW(toUTF16z(lname));
        if (h is null)
            throw new Exception("Failed to load DLL: " ~ name);
        m_modules[lname] = h;
        return h;
    }

    public static HMODULE getHandle(char[] name) {
        return m_modules[name];
    }

    public static ProcType getSymbol(ProcType)(char[] moduleName, char[] procName) {
        HMODULE handle = null;
        if (moduleName in m_modules)
            handle = m_modules[moduleName];
        else
            handle = registerModule(moduleName);

        assert(handle !is null);
        ProcType proc = cast(ProcType)GetProcAddress(handle, toStringz(procName));
        if (proc is null)
            throw new Exception("Cannot to get the address of " ~ procName);
        return proc;
    }
}

struct DllImport(char[] ModuleName, char[] ProcName, FT) {
    extern(Windows) alias ReturnType!(FT)
        function(ParameterTypeTuple!(FT))     FunctionType;
    alias DllImport!(ModuleName, ProcName, FT) SelfType;

    //FIXME: avoid the CTFE?
    private FunctionType m_funcPtr = null;

    public ReturnType!(FunctionType) opCall(ParameterTypeTuple!(FunctionType) args) {
        if (m_funcPtr is null)
            m_funcPtr = ModuleManager.getSymbol!(FunctionType)(ModuleName, ProcName);
        return m_funcPtr(args);
    }
}

/* void main()
{
    DllImport!("user32.dll", "MessageBoxA",
            int function(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)) MessageBox;

    MessageBox(null, "Text", "Title", MB_OK);
}    */